源码篇 - 广播接收者 此篇将重点介绍WorkManager使用的重要组件:广播接收者,主要涉及意外停止监听广播ForceStopRunnable.BroadcastReceiver
、约束状态监听广播ConstraintProxy.*
、启动重新规划服务的广播RescheduleReceiver
、代理约束更新广播ConstraintProxyUpdateReceiver
、测试诊断广播DiagnosticsReceiver
。此类组件为WorkManager稳定运行、重新规划、约束更新提供了支持。
ForceStopRunnable
WorkManager is restarted after an app was force stopped. Alarms and Jobs get cancelled when an application is force-stopped. To reschedule, we create a pending alarm that will not survive force stops.
强制停止应用程序后,WorkManager
将重新启动。当应用被强制停止时,Alarms
和Jobs
将被取消。为了重新安排,我们要创建一个无法通过强制停止的Alarm
。
在介绍ForceStopRunnable.BroadcastReceiver
前,我们先简单看一下它的外部类ForceStopRunnable
。
ForceStopRunnable
是一个Runnable对象,核心方法是run
, 其在WorkManager
初始化时调用WorkManagerImpl.internalInit()
, 构造并调用了ForceStopRunnable.run()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Override public void run () { WorkDatabasePathHelper.migrateDatabase(mContext); try { boolean needsScheduling = cleanUp(); if (shouldRescheduleWorkers()) { mWorkManager.rescheduleEligibleWork(); mWorkManager.getPreferenceUtils().setNeedsReschedule(false ); } else if (isForceStopped()) { mWorkManager.rescheduleEligibleWork(); } else if (needsScheduling) { Schedulers.schedule( mWorkManager.getConfiguration(), mWorkManager.getWorkDatabase(), mWorkManager.getSchedulers()); } mWorkManager.onForceStopRunnableCompleted(); } catch (SQLiteCantOpenDatabaseException | SQLiteDatabaseCorruptException | SQLiteAccessPermException exception) { String message = "The file system on the device is in a bad state. WorkManager cannot access " + "the app's internal data store." ; Logger.get().error(TAG, message, exception); throw new IllegalStateException(message, exception); } }
我们发现ForceStopRunable
主要职责是处理异常中断后对WorkManager中任务进行清理和重新规划,run()
主要干了3件事:
如果需要,将数据库迁移到不备份的目录
取消无效的JobScheduler作业/重新调度以前正在运行的作业
针对异常中断时WorkManager的状态进行不同的调度操作
那么他是如何判断是否是强制中断的呢,我们可以看下强制中断判断代码isForceStopped()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 @VisibleForTesting public boolean isForceStopped () { PendingIntent pendingIntent = getPendingIntent(mContext, FLAG_NO_CREATE); if (pendingIntent == null ) { setAlarm(mContext); return true ; } else { return false ; } }
通过判断闹钟广播是否存在来确定应用是否被强行停止,若不存在即闹钟Alarm被取消即是强制中断,此时将重新设置一个闹钟Alarm;
让我们来看看上文中的ForceStopRunnable.BroadcastReceiver
闹钟广播。
BroadcastReceiver
A {@link android.content.BroadcastReceiver} which takes care of recreating the long lived alarm which helps track force stops for an application. This is the target of the alarm set by ForceStopRunnable in {@link #setAlarm(Context)}. 一个{@link android.content.BroadcastReceiver}负责重新创建长寿命的Alarm,这有助于跟踪应用程序的强制停止。这是在{@link #setAlarm(Context)}中由forceoprunnable设置的Alarm目标。
从描述可知,此广播的主要目的是负责创建长期存活的闹钟,用以追踪应用程序的强制停止。
其代码相对简单,若收到action为ACTION_FORCE_STOP_RESCHEDULE
的广播,则设置一个长达十年的可唤醒设备的闹钟,然后再来一次(发送action为ACTION_FORCE_STOP_RESCHEDULE
的广播)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 private static PendingIntent getPendingIntent (Context context, int flags) { Intent intent = getIntent(context); return PendingIntent.getBroadcast(context, ALARM_ID, intent, flags); } @VisibleForTesting static Intent getIntent (Context context) { Intent intent = new Intent(); intent.setComponent(new ComponentName(context, ForceStopRunnable.BroadcastReceiver.class)); intent.setAction(ACTION_FORCE_STOP_RESCHEDULE); return intent; } static void setAlarm (Context context) { AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PendingIntent pendingIntent = getPendingIntent(context, FLAG_UPDATE_CURRENT); long triggerAt = System.currentTimeMillis() + TEN_YEARS; if (alarmManager != null ) { if (Build.VERSION.SDK_INT >= 19 ) { alarmManager.setExact(RTC_WAKEUP, triggerAt, pendingIntent); } else { alarmManager.set(RTC_WAKEUP, triggerAt, pendingIntent); } } } @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static class BroadcastReceiver extends android .content .BroadcastReceiver { @Override public void onReceive (Context context, Intent intent) { if (intent != null ) { String action = intent.getAction(); if (ACTION_FORCE_STOP_RESCHEDULE.equals(action)) { Logger.get().verbose( TAG, "Rescheduling alarm that keeps track of force-stops." ); ForceStopRunnable.setAlarm(context); } } } }
有前文可知,应用启动时会初始化WorkManager,WorkManager初始化则会执行ForceStopRunnable.run()
, 此时一般会调用setAlarm(), 创建Alarm,极端情况下会存在应用存活十年的情况,此BroadcastReceiver
即是用于处理这种情况的,当十年后又收到了这个广播,那么我们在创建一个十年期的闹钟Alarm,哦,天呐。
ConstraintProxy BatteryNotLowProxy BatteryChargingProxy StorageNotLowProxy NetworkStateProxy 上述BatteryNotLowProxy
、BatteryChargingProxy
、StorageNotLowProxy
、NetworkStateProxy
均是ConstraintProxy
的子类,实现均一致,仅有注册action不同,用以针对不同action的系统广播更新约束状态, 此处一并分析之。
以NetworkStateProxy
为例,当网络状态变化,应用收到"android.net.conn.CONNECTIVITY_CHANGE"
广播,触发NetworkStateProxy.onReceive()
:
1 2 3 4 5 6 @Override public void onReceive (Context context, Intent intent) { Logger.get().debug(TAG, String.format("onReceive : %s" , intent)); Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context); context.startService(constraintChangedIntent); }
调用CommandHandler.createConstraintsChangedIntent(context)
调起SystemAlarmService
, 其intent的action为"ACTION_CONSTRAINTS_CHANGED"
:
1 2 3 4 5 static Intent createConstraintsChangedIntent (@NonNull Context context) { Intent intent = new Intent(context, SystemAlarmService.class); intent.setAction(ACTION_CONSTRAINTS_CHANGED); return intent; }
由WorkManager基本使用及源码分析(三) - SystemAlarmService 讲述流程可知,最终会调用到CommandHandler.onHandleIntent()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void onHandleIntent(@NonNull Intent intent, int startId, @NonNull SystemAlarmDispatcher dispatcher) { String action = intent.getAction(); if (ACTION_CONSTRAINTS_CHANGED.equals(action)) { handleConstraintsChanged(intent, startId, dispatcher); } // 其他 } private void handleConstraintsChanged( @NonNull Intent intent, int startId, @NonNull SystemAlarmDispatcher dispatcher) { ConstraintsCommandHandler changedCommandHandler = new ConstraintsCommandHandler(mContext, startId, dispatcher); changedCommandHandler.handleConstraintsChanged(); }
此时intent的action为"ACTION_CONSTRAINTS_CHANGED"
,最终会调用到ConstraintsCommandHandler.handleConstraintsChanged()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void handleConstraintsChanged() { List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase() .workSpecDao() .getScheduledWork(); //更新约束代理以潜在地禁用先前完成的WorkSpecs的代理。 ConstraintProxy.updateAll(mContext, candidates); // 这需要在每个约束控制器中填充匹配的WorkSpec id。标记正在更新这些工作的状态 mWorkConstraintsTracker.replace(candidates); List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size()); // 筛选候选人应该已经规划好了。 long now = System.currentTimeMillis(); for (WorkSpec workSpec : candidates) { String workSpecId = workSpec.id; long triggerAt = workSpec.calculateNextRunTime(); // 时间条件符合且无约束或约束已符合,则加入符合条件集合 if (now >= triggerAt && (!workSpec.hasConstraints() || mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) { eligibleWorkSpecs.add(workSpec); } } for (WorkSpec workSpec : eligibleWorkSpecs) { String workSpecId = workSpec.id; Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId); // 告诉SystemAlarmDispatcher这个工作已经符合条件了,请给安排上 mDispatcher.postOnMainThread( new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId)); } // 重置正在更新这些工作状态的标记 mWorkConstraintsTracker.reset(); }
此方法先获取了规划中的工作生成集合,调用了ConstraintProxy.updateAll()
方法更新约束代理以潜在地禁用先前完成的WorkSpecs的代理,遍历判断每个WorkSpec是否符合条件,遍历符合约束条件的列表,告诉SystemAlarmDispatcher
添加任务,若SystemAlarmDispatcher
中无任务则分发执行任务(还记得processCommand()是一个环状的轮询吗?)。
SystemAlarmDispatcher.AddRunnable()
后续调用SystemAlarmDispatcher.add()
在WorkManager基本使用及源码分析(三) - SystemAlarmService 中有详细说明,此处仅摘要流程。SystemAlarmDispatcher.AddRunnable()
-> SystemAlarmDispatcher.add()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public boolean add (@NonNull final Intent intent, final int startId) { String action = intent.getAction(); if (TextUtils.isEmpty(action)) { return false ; } if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action) && hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) { return false ; } intent.putExtra(KEY_START_ID, startId); synchronized (mIntents) { boolean hasCommands = !mIntents.isEmpty(); mIntents.add(intent); if (!hasCommands) { processCommand(); } } return true ; } static class AddRunnable implements Runnable { @Override public void run () { mDispatcher.add(mIntent, mStartId); } }
ConstraintProxyUpdateReceiver 上面讲到ConstraintsCommandHandler.handleConstraintsChanged()
第一步会调用ConstraintProxy.updateAll(mContext, candidates)
最终会发送广播给ConstraintProxyUpdateReceiver
更新约束代理以潜在地禁用先前完成的WorkSpecs的代理,我们简单过一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 static void updateAll(Context context, List<WorkSpec> workSpecs) { boolean batteryNotLowProxyEnabled = false; boolean batteryChargingProxyEnabled = false; boolean storageNotLowProxyEnabled = false; boolean networkStateProxyEnabled = false; // 查找每个WorkSpec(是否有)约束条件状态 for (WorkSpec workSpec : workSpecs) { Constraints constraints = workSpec.constraints; batteryNotLowProxyEnabled |= constraints.requiresBatteryNotLow(); batteryChargingProxyEnabled |= constraints.requiresCharging(); storageNotLowProxyEnabled |= constraints.requiresStorageNotLow(); networkStateProxyEnabled |= constraints.getRequiredNetworkType() != NOT_REQUIRED; // 知道需要全部广播接收者,就不用判断了 if (batteryNotLowProxyEnabled && batteryChargingProxyEnabled && storageNotLowProxyEnabled && networkStateProxyEnabled) { break; } } Intent updateProxyIntent = ConstraintProxyUpdateReceiver.newConstraintProxyUpdateIntent( context, batteryNotLowProxyEnabled, batteryChargingProxyEnabled, storageNotLowProxyEnabled, networkStateProxyEnabled); // ConstraintProxies are being updated via a separate broadcast receiver. // For more information on why we do this look at b/73549299 context.sendBroadcast(updateProxyIntent); }
这里先是判断需要哪些广播接收者,会发送广播给ConstraintProxyUpdateReceiver,最终会调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Override public void onReceive(@NonNull final Context context, @Nullable final Intent intent) { String action = intent != null ? intent.getAction() : null; if (!ACTION.equals(action)) { } else { final PendingResult pendingResult = goAsync(); WorkManagerImpl workManager = WorkManagerImpl.getInstance(context); TaskExecutor taskExecutor = workManager.getWorkTaskExecutor(); taskExecutor.executeOnBackgroundThread(new Runnable() { @Override public void run() { try { // 在后台线程上执行此操作,因为使用PackageManager来启用或禁用代理涉及到对文件系统的写操作。 boolean batteryNotLowProxyEnabled = intent.getBooleanExtra( KEY_BATTERY_NOT_LOW_PROXY_ENABLED, false); boolean batteryChargingProxyEnabled = intent.getBooleanExtra( KEY_BATTERY_CHARGING_PROXY_ENABLED, false); boolean storageNotLowProxyEnabled = intent.getBooleanExtra( KEY_STORAGE_NOT_LOW_PROXY_ENABLED, false); boolean networkStateProxyEnabled = intent.getBooleanExtra( KEY_NETWORK_STATE_PROXY_ENABLED, false); Logger.get().debug( TAG, String.format("Updating proxies: BatteryNotLowProxy enabled (%s), " + "BatteryChargingProxy enabled (%s), " + "StorageNotLowProxy (%s), " + "NetworkStateProxy enabled (%s)", batteryNotLowProxyEnabled, batteryChargingProxyEnabled, storageNotLowProxyEnabled, networkStateProxyEnabled)); PackageManagerHelper.setComponentEnabled(context, BatteryNotLowProxy.class, batteryNotLowProxyEnabled); PackageManagerHelper.setComponentEnabled(context, BatteryChargingProxy.class, batteryChargingProxyEnabled); PackageManagerHelper.setComponentEnabled(context, StorageNotLowProxy.class, storageNotLowProxyEnabled); PackageManagerHelper.setComponentEnabled(context, NetworkStateProxy.class, networkStateProxyEnabled); } finally { pendingResult.finish(); } } }); } }
主要执行了获取前文提供的需要哪些约束广播接收器,调用PackageManagerHelper.setComponentEnabled(context, StorageNotLowProxy.class, storageNotLowProxyEnabled)
更改广播接收者注册信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 /** * 使用{@link PackageManager} 去开/关manifest声明的组件 * * @param context {@link Context} * @param klazz The class of component * @param enabled {@code true} 开关组件 */ public static void setComponentEnabled( @NonNull Context context, @NonNull Class<?> klazz, boolean enabled) { try { PackageManager packageManager = context.getPackageManager(); ComponentName componentName = new ComponentName(context, klazz.getName()); packageManager.setComponentEnabledSetting(componentName, enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } catch (Exception exception) { } }
RescheduleReceiver
Reschedules alarms on BOOT_COMPLETED and other similar scenarios.
在BOOT_COMPLETED和其他类似场景下重新调度Alarm。
DiagnosticsReceiver
The {@link android.content.BroadcastReceiver} which dumps out useful diagnostics information. 输出有用的诊断信息